//TO DO 
	// READ RX DATA AND DETERMINE RECEIVED CODE AND INFORM DELEGATE

import java.awt.List;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;

import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;


import com.rapplogic.xbee.api.AtCommand;
import com.rapplogic.xbee.api.XBee;
import com.rapplogic.xbee.api.XBeeAddress64;
import com.rapplogic.xbee.api.XBeeException;
import com.rapplogic.xbee.api.XBeeResponse;
import com.rapplogic.xbee.api.XBeeTimeoutException;
import com.rapplogic.xbee.api.wpan.TxRequest64;


public class Transceiver implements Runnable{
	
	Main delegateMain;

	private int baud;
	private String COM;
	private XBee xbee = new XBee();

	public volatile boolean RX;
	public volatile boolean TX;
	public static boolean clear;

	public volatile Point[] txPacket;
	public volatile byte dockingCircle;
	public volatile int txType = 0; // 0 - route of POINTS, 1 - landing byte
	public volatile byte _parameter;
	public byte parameter;
	
	public Transceiver instance = this;
	
	public Transceiver(int _baud,String _COM,Main $delegate){
		baud = _baud;
		COM = _COM;
		RX = true;
		clear = true;
		delegateMain = $delegate;
	}
	
	public void tx() throws InterruptedException{
		
		Thread.sleep(3000);
		
		int []payload; 
		parameter = _parameter;
		
		switch(txType){
		case TxType.route:
			payload = RouteTX();
			break;
		case TxType.dockingData:
			payload = dockingData();
			break;
		case TxType.dockNotFound:
			payload = dockNotFound();
		case TxType.remote:
			payload = remoteControl();
		default:
			payload = RouteTX();
		}
		
		System.out.println("transmiting!!!!!!!!!!!!!");
		try {	
			xbee.open(COM, baud);
			
			//SH + SL of end device
			XBeeAddress64 addr64 = new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x9f, 0x0e, 0x52);
			TxRequest64 request = new TxRequest64(addr64,payload);	
			xbee.sendAsynchronous(request);
			delegateMain.progressBar.setValue(100);
			delegateMain.progressBar.setString("Successful TX");
			
		} catch (XBeeException e) {
			
				System.out.println("tx command failed "+ e.getCause());
				delegateMain.progressBar.setValue(0);
				delegateMain.progressBar.setString("Error");
				
		} catch(Exception e) {
			System.out.println("tx command failed Exception "+ e.getCause());
		}
		 finally {
			//delegateMain.startRX();
			RX = true;
			xbee.close();
		}
		
		
	}
	
	private int[] dockingData() {
		
		ByteBuffer buffer = ByteBuffer.allocate(1+1+(1)+1+1);//command - params - data - [footer - footer]
		buffer.put(CommandList.DOCKINGOFFSETS);
		buffer.put((byte) 0);
		buffer.put(dockingCircle);
		buffer.put((byte) 0);
		buffer.put((byte) 0);
		
		int[] packet = pack(buffer);
		
	    return packet;		
	}

	
	private int[] dockNotFound(){
		ByteBuffer buffer = ByteBuffer.allocate(1+1+(1)+1+1);//command - data - [footer - footer]
		buffer.put(CommandList.DOCKNOTFOUND);
		buffer.put((byte) 0); 
		buffer.put((byte) 0);
		buffer.put((byte) 0);
		buffer.put((byte) 0);
		
		int[] packet = pack(buffer);
		
	    return packet;	
	}
	
	private int[] remoteControl(){
		ByteBuffer buffer = ByteBuffer.allocate(1+1+(1)+1+1);//command - data - [footer - footer]
		buffer.put(CommandList.CONTROL);
		
		buffer.put(parameter);
		
		buffer.put((byte) 0);
		buffer.put((byte) 0);
		buffer.put((byte) 0);
		
		int[] packet = pack(buffer);
		
	    return packet;	
	}
	
	public void startRX(){
		RX = true;
	}
	
	public int[] pack(ByteBuffer buffer){
		byte[] bytearray = buffer.array();
        int[] packet = new int[bytearray.length];
       
        System.out.println(buffer.array().length);
        
       
        
        for(int i=0; i<buffer.array().length; i++){
        	packet[i] = bytearray[i];
        	 
        }
        return packet;
	}
	//only data received i.e. ( only data UAV transmits) is GPS/current position data
	public void rx() throws InterruptedException{
		
		/*while(!clear){
			
		}
		clear = false;*/
		Thread.sleep(1000);
		
		try {	
			xbee.open(COM, baud);
			boolean timedOut;
			
			while(RX){
				System.out.println("rx listening");
				timedOut = false;
				XBeeResponse response = null;
				try{
				response = xbee.getResponse(1000);//blocking
				
				} catch (XBeeTimeoutException e){
					timedOut = true;
				}
				
				if(!timedOut){
				    int[] data = response.getProcessedPacketBytes();
				    
				    
				    if(data.length < 20){
				    	delegateMain.setPane("RX: \n Packet smaller than expected (20):\n received - "+ data.length + " bytes");
				    }
				    
				    
				    readInput(data);
				    //header data checksum
				    //- XBee packet output [7 bytes][12 bytes][1 byte] : expected 20
				    //data
				    	//[(byte) command][(byte)params][(8byte){lat,long}][(byte) 0 ][(byte) 0 ] 
				   
				    
			    }
			    
			}
			
		} catch (Exception e) {
			System.out.println("rx command failed"+ e);
		} finally {
			xbee.close();
			//clear = true;
		}
		
		if(RX){
			System.out.println("exiting on RX = true ERROR!!");
		}else {
			System.out.println("exiting on RX = false woooh!!");
		}
	}
	
	
	//reads incoming transmissions and handles according to headers
	//there should only be two kinds of incoming transmissions,


	public void readInput(int[] data){
		
		if(data[0] == CommandList.FINDDOCK){
			
			this.delegateMain.findLandingPad();
			
		}if(data[0] == CommandList.GPSUPDATE){
			
			 float rx_lat = 0;
			    float rx_lon = 0;
			    
			    rx_lat = data[9]<<8*3 | data[10]<<8*2 | data[11]<<8 | data[12];
			    rx_lon = data[13]<<8*3 | data[14]<<8*2 | data[15]<<8 | data[16];
			    
			    delegateMain.GPSupdate(rx_lat,rx_lon);
		}
		
	}
	
	
	
	
	/*DOUBLE route size capacity without implementing data buffers
	 * CONSIDER MAKING POINTS FLOATS (MORE APPRORIATE THAN DOUBLES) IF THE GPS
	 * MODULE DOESN'T HAVE THE RESOLUTION TO SUPPORT THAT DEGREE OF ACCURACY
	 * 
	 */
	
	public int[] RouteTX(){
		//route# presents redundancy since we have an ACK system
		//packet =>  command(byte) - points(doubles)  
		
		
		//FROM DANIELS OUTLINED FLIGHT LOGIC
		/*" // when there is a route to be downloaded, download it
                // first and last point should be the location of the charging station
                // last element of route array should be 0 (NULL)"
                 * 
                 * -Daniel
                 */
		//API is limited to 100 bytes per transmission
		ByteBuffer buffer = ByteBuffer.allocate(1+1+(txPacket.length*4*2)+1+1); //command,route[], "0 (NULL)" 
		buffer.put(CommandList.ROUTE);
        buffer.put((byte) txPacket.length);
        
        //progress
        delegateMain.progressBar.setValue(5);
        int progress = 10/txPacket.length;
        //progress
        
        //while(txPacket.postPoint
        
        for(Point p : txPacket){
        	
        	System.out.println(Float.toHexString(p.latitude));
        	int bits = Float.floatToIntBits(p.latitude);
        	byte[] bytes = new byte[4];
        	bytes[0] = (byte)(bits & 0xff);
        	bytes[1] = (byte)((bits >> 8) & 0xff);
        	bytes[2] = (byte)((bits >> 16) & 0xff);
        	bytes[3] = (byte)((bits >> 24) & 0xff);
        	
        	buffer.put(bytes[3]);
        	buffer.put(bytes[2]);
        	buffer.put(bytes[1]);
        	buffer.put(bytes[0]);
        	
        	
        	System.out.println(Float.toHexString(p.longitude));
        	bits = Float.floatToIntBits(p.longitude);
        	bytes = new byte[4];
        	bytes[0] = (byte)(bits & 0xff);
        	bytes[1] = (byte)((bits >> 8) & 0xff);
        	bytes[2] = (byte)((bits >> 16) & 0xff);
        	bytes[3] = (byte)((bits >> 24) & 0xff);
        	
        	buffer.put(bytes[3]);
        	buffer.put(bytes[2]);
        	buffer.put(bytes[1]);
        	buffer.put(bytes[0]);
        	
        	delegateMain.progressBar.setValue(delegateMain.progressBar.getValue()+progress);
        }
        
        buffer.put((byte) 0);
        buffer.put((byte) 0);
        
        byte[] byteArray = buffer.array();
        /*IntBuffer intBuf =
        		   ByteBuffer.wrap(byteArray)
        		     .order(ByteOrder.BIG_ENDIAN)
        		     .asIntBuffer();
        
        int[] array = new int[intBuf.remaining()];
        intBuf.get(array);
        */
        System.out.println("+++++++++++++");
        for (byte b : byteArray) {
            System.out.println("c:" + (char) b + "-> "
                   + Integer.toBinaryString(b));
            
            //System.out.printf("-%x-",b);
        }/*
        System.out.println("+++++++++++++");
        for(int i : array){
        	System.out.println("c:" + (char) i + "-> "
                    + Integer.toBinaryString(i));
        	
        }*/
        System.out.println("+++++++++++++");
        
        
        int[] packet = pack(buffer);
        return packet;
	}
	
	public void stopRX(){//must be called outside of thread 
		RX = false;
		TX = true;
	}

	@Override
	public void run() {
		PropertyConfigurator.configure("log4j.properties");
		while(true){
			
			
		//	if(RX){
		//		System.out.println("RX is true");
		//	}
			
				try {
					rx();
					tx();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
		}	
		//	new Thread(instance = new Transceiver(baud,COM,delegateMain));
		//	instance.run();
				
		//	break;
		//}
		
		
	}

	


}
